﻿/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using System.Qizx.Api;
using System.Collections;
using System.Xml;
using System.IO;
using DataModelException = System.Qizx.Api.DataModelException;
using EvaluationException = System.Qizx.Api.EvaluationException;
using Node = System.Qizx.Api.Node;
using QName = System.Qizx.Api.QName;
using System.Qizx.Api;
using QizxException = System.Qizx.Api.QizxException;
using XMLPullStream = System.Qizx.Api.XMLPullStream;
using PushStreamToDOM = System.Qizx.Api.Util.PushStreamToDOM;
using XQDateTimeBase = System.Qizx.Api.Util.XQDateTimeBase;
using DateTimeException = System.Qizx.Api.Util.XQDateTimeException;
using Duration = System.Qizx.Api.Util.XQDuration;
using NamespaceContext = System.Qizx.Util.NamespaceContext;
using Comparison = System.Qizx.Util.Basic.Comparison;
using FileUtil = System.Qizx.Util.Basic.FileUtil;
using ComparisonContext = System.Qizx.XQuery.ComparisonContext;
using XQItem = System.Qizx.XQuery.XQItem;
using XQValue = System.Qizx.XQuery.XQValue;
using StringValue = System.Qizx.XQuery.Dt.StringValue;
using UntypedAtomicType = System.Qizx.XQuery.Dt.UntypedAtomicType;
using URI = System.Uri;
using System.Xml.Schema;

namespace System.Qizx.Xdm {
    [Serializable]
    public class XdmElement : XdmNode {
        private static Node[] EmptyNodes;
        public static readonly QName nil = IQName.GetQName(XmlSchema.InstanceNamespace, "nil");

        static XdmElement() {
            EmptyNodes = new Node[0];
        }

        public virtual bool IsNil {
            get {
                string nil = this.GetAttribute(XdmElement.nil);
                return nil == "true" || nil == "1";
            }
            set {
                if (this.IsNil != value) {
                    if (value)
                        SetAttribute(XdmElement.nil, "true");
                    else
                        RemoveAttributeNode(XdmElement.nil);
                }
            }
        }

        internal XdmElement(XdmName name, bool empty, XdmDocument doc)
            : this(doc.DataModel, name.InternalQName) {
            _name = name;
        }

        public override string NamespaceURI {
            get {
                return _name.NamespaceURI;
            }
        }

        public override string LocalName {
            get {
                return _name.LocalName;
            }
        }

        public override string Prefix {
            get {
                return _name.Prefix;
            }
            set {
                this._name = this._name.OwnerDocument.AddXmlName(value, this.LocalName, this.NamespaceURI, null);
            }
        }

        public override string Name {
            get {
                if (_name == null) return string.Empty;
                return _name.Name;
            }
        }


        #region public constructor
        protected internal XdmElement(string qualifiedName, string namespaceURI, XdmDocument ownerDoc) :
            this(ownerDoc.AddXmlName(qualifiedName, namespaceURI, null), true, ownerDoc) { }
        protected internal XdmElement(string qualifiedName, string namespaceURI, XdmDocument ownerDoc, IXdmSchemaInfo schemaInfo) :
            this(ownerDoc.AddXmlName(qualifiedName, namespaceURI, schemaInfo), true, ownerDoc) { }
        protected internal XdmElement(string prefix, string localName, string namespaceURI, XdmDocument ownerDoc) :
            this(ownerDoc.AddXmlName(prefix, localName, namespaceURI, null), true, ownerDoc) { }
        protected internal XdmElement(string prefix, string localName, string namespaceURI, XdmDocument ownerDoc, IXdmSchemaInfo schemaInfo) :
            this(ownerDoc.AddXmlName(prefix, localName, namespaceURI, schemaInfo), true, ownerDoc) { }
        #endregion

        public override string NodeKind {
            get {
                return "element";
            }

        }

        /// <summary>
        /// Removes all.
        /// </summary>
        public override void RemoveAll() {
            base.RemoveAll();
            this._lastChild = null;
            this._attributes = null;
        }

        public override string StringValue {
            get {
                System.Text.StringBuilder buf = new System.Text.StringBuilder();
                BasicNode firstNode = this.GetFirstChild();
                for (BasicNode kid = firstNode; kid != null; kid = kid.NextSibling)
                    RecStringValue(kid, buf);
                string text = buf.ToString();
                if (firstNode == null && string.IsNullOrEmpty(text)) {
                    try {
                        if (this.SchemaInfo != null && this.SchemaInfo.SchemaElement != null) {
                            text = this.SchemaInfo.SchemaElement.DefaultValue;
                            return text == null ? string.Empty : text;
                        }
                    } catch {
                    }

                }
                return text;
            }
        }

        public override string ValueAsString {
            get {
                //HACK CoShare
                if (this.SchemaInfo.DataType == null && this.SchemaInfo.SchemaElement != null) {
                    throw new EvaluationException(IQName.GetQName(NamespaceContext.ERR, "FOTY0012"), "argument node '" + this.Name + "' does not have a typed value.");
                }
                return base.ValueAsString;
            }
        }

        public override QName NodeName {
            get {
                return _name.InternalQName;
            }
        }

        public override int DefinedNSCount {
            get {
                int cnt = 0;
                for (XdmNode attr = _attributes; attr != null; attr = attr._nextSibling)
                    if (attr.NodeType == System.Qizx.Api.XdmNodeType.Namespace)
                        ++cnt;
                return cnt;
            }

        }
        private XdmNode FirstNS {
            // return first NSnode on element
            get {
                XdmNode attr = _attributes;
                for (; attr != null; attr = attr._nextSibling)
                    if (attr.NodeType == XdmNodeType.Namespace)
                        return attr;
                return null;
            }

        }
        public override XdmNodeType NodeType {
            // ---- extensions for internal use:


            get {
                return System.Qizx.Api.XdmNodeType.Element;
            }

        }
        public override bool Atom {
            get {
                return false;
            }

        }

        public override int AttributeCount {
            get {
                int cnt = 0;
                for (XdmNode attr = _attributes; attr != null; attr = attr._nextSibling)
                    if (attr.NodeType == System.Qizx.Api.XdmNodeType.Attribute)
                        ++cnt;
                return cnt;
            }

        }

        //private IQName name;
        private XdmName _name;

        internal XdmNode _lastChild;


        // list of attributes AND NS nodes: attr first, then NS
        internal XdmAttribute _attributes;

        internal XdmElement(CoreDataModel model, IQName name)
            : base(model) {
            XdmDocument doc = model.OwnerDocument;
            if (this.NodeType == XdmNodeType.Document) {
                model._ownerDocument = (XdmDocument)this;
            } else {
                _name = doc.AddXmlName(name.Prefix, name.Name, name.Namespace, null);
            }

        }

        private void RecStringValue(BasicNode node, System.Text.StringBuilder buf) {
            if (node.IsElement) {
                BasicNode kid = node.GetFirstChild();
                for (; kid != null; kid = kid.NextSibling)
                    RecStringValue(kid, buf);
            } else if (node.NodeType == System.Qizx.Api.XdmNodeType.Text)
                // ignore comments and PI
                buf.Append(node.StringValue);
        }

        public override NodeSequenceBase GetChildren() {
            return new SSequence(GetFirstChild() as XdmNode);
        }

        internal override BasicNode GetFirstChild() {
            XdmNode lastNode = _lastChild;
            if (lastNode != null) {
                return lastNode._nextSibling;
            }
            return null;
        }

        public override string GetPrefixOfNamespace(string nsuri) {
            for (XdmNode attr = this.FirstNS; attr != null; attr = attr._nextSibling) {
                XdmNSAttribute ns = (XdmNSAttribute)attr;
                if (nsuri.Equals(ns._strValue))
                    return ns._name.LocalName;
            }
            //support subclass can override GetNamespacePrefix
            if (this._parent != null) {
                return _parent.GetPrefixOfNamespace(nsuri);
            }
            return null;
        }

        public override string GetNamespaceOfPrefix(string prefix) {

            for (XdmNode attr = this.FirstNS; attr != null; attr = attr._nextSibling) {
                XdmNSAttribute ns = (XdmNSAttribute)attr;
                if (prefix.Equals(ns._name.LocalName))
                    return ns.StringValue;
            }
            //support subclass can override GetNamespaceUri
            if (this._parent != null) {
                return _parent.GetNamespaceOfPrefix(prefix);
            }
            return null;
        }

        public override void AddInScopeNamespacesTo(NamespaceContext nsContext) {
            RecAddInscopeNS(nsContext);
        }

        private void RecAddInscopeNS(NamespaceContext nsContext) {
            if (_parent != null)
                _parent.RecAddInscopeNS(nsContext);
            //             else if(inScopeNS != null) {
            //                 // NS ihnerited from static context
            //                 for(int n = 0, cnt = inScopeNS.getLocalSize(); n < cnt; n++)
            //                 nsContext.addMapping(inScopeNS.getLocalPrefix(n),
            //                                      inScopeNS.getLocalNamespaceURI(n));
            //             }
            AddNamespacesTo(nsContext);
        }

        public override int AddNamespacesTo(NamespaceContext nsContext) {
            int count = 0;
            XdmNode nsNode = FirstNS;
            for (; nsNode != null; nsNode = nsNode._nextSibling) {
                XdmNSAttribute ns = (XdmNSAttribute)nsNode;
                nsContext.addMapping(ns._name.LocalName, ns.StringValue);
                ++count;
            }
            return count;
        }

        public override int AddUsedNamespacesTo(NamespaceContext nsContext) {
            int count = 0;
            QName name = this.NodeName;
            string ns = name.Namespace;
            if ((System.Object)ns != (System.Object)"") {
                nsContext.addMapping(GetPrefixOfNamespace(ns), ns);
                ++count;
            }
            XdmNode attr = _attributes;
            for (; attr != null; attr = attr._nextSibling) {
                name = attr.NodeName;
                ns = name.Namespace;
                if ((System.Object)ns != (System.Object)"" && attr.NodeType == System.Qizx.Api.XdmNodeType.Attribute) {
                    nsContext.addMapping(GetPrefixOfNamespace(ns), ns);
                    ++count;
                }
            }
            return count;
        }

        public override bool IsElement {
            get { return true; }
        }

        internal virtual bool AttributesEq(BasicNode that, System.Globalization.CompareInfo collator) {
            Node[] oattr = that.Attributes;
            if (oattr == null)
                return AttributeCount == 0;
            if (oattr.Length != AttributeCount)
                return false;
            for (int a = oattr.Length; --a >= 0; ) {
                Node oatt = oattr[a];
                QName attrName = oatt.NodeName;
                XdmNode att = (XdmNode)GetAttributeNode(attrName);
                if (att == null || !att.ValueEq(oatt, collator))
                    return false;
            }
            return true;
        }

        internal virtual bool ContentEq(BasicNode that, System.Globalization.CompareInfo collator) {
            BasicNode okid = that.GetFirstChild();
            for (XdmNode kid = this.GetFirstChild() as XdmNode; kid != null; kid = kid.NextSibling as XdmNode) {
                XdmNodeType kidkind = kid.NodeType, okidkind;
                if (kidkind == System.Qizx.Api.XdmNodeType.Comment || kidkind == System.Qizx.Api.XdmNodeType.ProcessingInstruction)
                    continue;

                for (; ; okid = okid.NextSibling) {
                    if (okid == null)
                        return false;
                    if ((okidkind = okid.NodeType) != System.Qizx.Api.XdmNodeType.Comment && okidkind != System.Qizx.Api.XdmNodeType.ProcessingInstruction)
                        break;
                }
                // we have 2 comparable kids:
                if (!kid.DeepEq(okid, collator))
                    return false;
                okid = okid.NextSibling;
            }
            // just check that there are no more kids in 'that'
            return okid == null;
        }

        public override Node GetAttributeNode(XmlQualifiedName name) {
            XdmNode attr = _attributes;
            for (; attr != null; attr = attr._nextSibling)
                if (attr.NodeName.Equals(name) && attr.NodeType == System.Qizx.Api.XdmNodeType.Attribute)
                    return attr;
            return null;
        }

        public string GetAttribute(string name, string nsUri) {
            XdmNode attr = _attributes;
            for (; attr != null; attr = attr._nextSibling)
                if (attr.NodeName.Name == name && attr.NodeName.Namespace == nsUri && attr.NodeType == System.Qizx.Api.XdmNodeType.Attribute)
                    return attr.ValueAsString;
            return null;
        }

        /// <summary>
        /// Gets the attribute.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public string GetAttribute(QName name) {
            XdmNode attr = _attributes;
            for (; attr != null; attr = attr._nextSibling)
                if (name.Equals(attr.NodeName))
                    return attr.ValueAsString;
            return null;
        }

        public virtual bool HasAttributes {
            get {
                if (this._attributes == null) {
                    return false;
                }
                return true;
            }
        }

        /// <summary>
        /// Removes the attribute node.
        /// </summary>
        /// <param name="oldAttr">The old attr.</param>
        /// <returns></returns>
        public virtual XdmAttribute RemoveAttributeNode(XdmAttribute oldAttr) {
            return RemoveAttributeNode(oldAttr.LocalName, oldAttr.NamespaceURI);
        }

        public virtual XdmAttribute RemoveAttributeNode(string localName, string namespaceURI) {
            XdmNode attr = _attributes;
            XdmNode prevAttr = null;
            XdmAttribute oldAttr = null;
            for (; attr != null; attr = attr._nextSibling) {
                if (attr.NodeName.Name == localName && attr.NodeName.Namespace == namespaceURI) {
                    oldAttr = attr as XdmAttribute;
                    if (prevAttr != null) {
                        prevAttr._nextSibling = attr._nextSibling;
                    } else {
                        _attributes = attr._nextSibling as XdmAttribute;
                    }
                    break;
                }
                prevAttr = attr;
            }
            if (oldAttr != null) oldAttr._parent = null;
            return oldAttr;
        }

        public virtual XdmAttribute RemoveAttributeNode(QName name) {
            return RemoveAttributeNode(name.Name, name.Namespace);
        }

        /// <summary>
        /// Sets the attribute.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        public virtual void SetAttribute(QName name, string value) {
            XdmNode attr = _attributes;
            for (; attr != null; attr = attr._nextSibling) {
                if (name.Equals(attr.Name)) {
                    XdmAttribute xdmAttr = attr as XdmAttribute;
                    if (xdmAttr != null) {
                        xdmAttr.TextValue = value;
                        return; //return;
                    }
                }
            }
            string prefix = this.GetPrefixOfNamespace(name.Namespace);
            XdmAttribute newAttr = OwnerDocument.CreateAttribute(prefix, name.Name, name.Namespace);
            newAttr.TextValue = value;
            this.AddAttribute(newAttr);
        }

        public virtual void SetAttribute(string localName, string namespaceURI, string value) {
            XdmNode attr = _attributes;
            for (; attr != null; attr = attr._nextSibling) {
                if (attr.NodeName.Name == localName && attr.NodeName.Namespace == namespaceURI) {
                    XdmAttribute xdmAttr = attr as XdmAttribute;
                    if (xdmAttr != null) {
                        xdmAttr.TextValue = value;
                        return;//setted
                    }
                }
            }
            string prefix = this.GetPrefixOfNamespace(namespaceURI);
            XdmAttribute newAttr = OwnerDocument.CreateAttribute(prefix, localName, namespaceURI);
            newAttr.TextValue = value;
            this.AddAttribute(newAttr);
        }

        public string GetAttribute(string name) {
            return GetAttribute(name, string.Empty);
        }

        public override Node[] Attributes {
            get {
                int cnt = AttributeCount;
                if (cnt == 0)
                    return EmptyNodes;
                Node[] attrs = new Node[cnt];
                XdmNode attr = _attributes;
                for (int a = 0; attr != null; attr = attr._nextSibling)
                    if (attr.NodeType == System.Qizx.Api.XdmNodeType.Attribute)
                        attrs[a++] = attr;
                return attrs;
            }
        }


        public virtual void SetAttributes(XdmAttribute attr) {
            _attributes = attr;
            for (; attr != null; attr = (XdmAttribute)attr._nextSibling)
                attr._parent = this;
        }

        // does not check
        public virtual bool AddNamespace(XdmNSAttribute ns) {
            XdmAttribute last = null, n = _attributes;
            // look for first NS, note previous node
            for (; n != null; last = n, n = (XdmAttribute)n._nextSibling) {
                if (n.NodeType == System.Qizx.Api.XdmNodeType.Namespace) {
                    if (n.NodeName == ns.NodeName)
                        return false; // duplicate
                } // dont break: go to end
            }
            if (last == null) {
                // no attr: insert before other NS
                ns._nextSibling = _attributes;
                _attributes = ns;
            } else {
                ns._nextSibling = last._nextSibling;
                last._nextSibling = ns;
            }
            return true;
        }

        // now only real attribute, no more NS
        // Attributes come first, then NS nodes
        public virtual bool AddAttribute(XdmAttribute attr) {
            if (attr.NodeType == XdmNodeType.Namespace) {
                return AddNamespace(attr as XdmNSAttribute);
            }
            if (XdmAttribute.id.Equals(attr.NodeName))
                this.OwnerDocument.AddIdAttribute(attr);

            XdmAttribute last = null, n = _attributes;
            // look for first NS, note previous node
            for (; n != null; last = n, n = (XdmAttribute)n._nextSibling) {
                if (n.NodeType == System.Qizx.Api.XdmNodeType.Namespace)
                    break;
                // check duplicate:
                if (n._name.InternalQName == attr._name.InternalQName) {
                    n._strValue = attr._strValue;
                    return false; // duplicate
                }
            }
            if (last == null) {
                // no attr: insert before other NS
                attr._nextSibling = _attributes;
                _attributes = attr;
            } else {
                attr._nextSibling = last._nextSibling;
                last._nextSibling = attr;
            }
            attr._parent = this;
            return true;
        }

        public override void AddText(string value) {
            throw new System.SystemException("addText on an element");
        }

        #region Extension methods
        public override void AppendChild(XdmNode newChild) {
            XdmDocument doc = this.OwnerDocument;
            if (doc == null)
                doc = this as XdmDocument;

            if (this == newChild || AncestorNode(newChild)) {
                return;
            }
            if (newChild._parent != null) {
                newChild._parent.RemoveChild(newChild);
            }

            XdmDocument document2 = newChild.OwnerDocument;
            if (((document2 != null) && (document2 != doc)) && (document2 != this)) {
                return;
            }

            XdmNode lastNode = this.LastChild;
            XdmNode nextNode = newChild;
            if (lastNode == null) {
                nextNode._nextSibling = nextNode;
                this._lastChild = nextNode;
                nextNode._parent = this;
            } else {
                nextNode._nextSibling = lastNode._nextSibling;
                lastNode._nextSibling = nextNode;
                _lastChild = nextNode;
                nextNode._parent = this;
                //if (lastNode.IsText && nextNode.IsText) {
                //    NestTextNodes(lastNode, nextNode);
                //}
            }
        }

        /// <summary>
        /// Inserts the specified node immediately after the specified reference node.
        /// </summary>
        /// <param name="newChild">The new child.</param>
        /// <param name="refChild">The ref child.</param>
        /// <returns></returns>
        public virtual XdmNode InsertAfter(XdmNode newChild, XdmNode refChild) {
            if (refChild == null)
                throw new ArgumentNullException("refChild is null.");
            XdmDocument doc = this.OwnerDocument;
            if (doc == null)
                doc = this as XdmDocument;

            if (this == newChild || AncestorNode(newChild)) {
                return newChild;
            }
            if (newChild._parent != null) {
                newChild._parent.RemoveChild(newChild);
            }

            XdmDocument document2 = newChild.OwnerDocument;
            if (((document2 != null) && (document2 != doc)) && (document2 != this)) {
                return newChild;
            }
            if (refChild == null || refChild.ParentNode != this) {
                throw new ArgumentException("refChild is not a child node of current node.");
            }

            XdmNode nextNode = refChild._nextSibling;
            refChild._nextSibling = newChild;
            newChild._nextSibling = nextNode;
            if (refChild == _lastChild)
                _lastChild = newChild;
            newChild._nextSibling = refChild;
            newChild._parent = this;
            if (refChild == _lastChild)
                _lastChild = newChild;
            return newChild;
        }

        /// <summary>
        /// Inserts the specified node immediately before the specified reference node.
        /// </summary>
        /// <param name="newChild">The new child.</param>
        /// <param name="refChild">The ref child.</param>
        /// <returns></returns>
        public override XdmNode InsertBefore(XdmNode newChild, XdmNode refChild) {
            if (refChild == null)
                throw new ArgumentNullException("refChild is null.");
            XdmDocument doc = this.OwnerDocument;
            if (doc == null)
                doc = this as XdmDocument;

            if (this == newChild || AncestorNode(newChild)) {
                return newChild;
            }
            if (newChild._parent != null) {
                newChild._parent.RemoveChild(newChild);
            }

            XdmDocument document2 = newChild.OwnerDocument;
            if (((document2 != null) && (document2 != doc)) && (document2 != this)) {
                return newChild;
            }
            if (refChild == null || refChild.ParentNode != this) {
                throw new ArgumentException("refChild is not a child node of current node.");
            }
            XdmNode prevNode = refChild.PreviousSibling as XdmNode;
            newChild._nextSibling = refChild;
            newChild._parent = this;
            if (prevNode != null)
                prevNode._nextSibling = newChild;
            else
                _lastChild._nextSibling = newChild;

            return newChild;
        }

        /// <summary>
        /// Replaces the child node oldChild with newChild node.
        /// </summary>
        /// <param name="newChild">The new child.</param>
        /// <param name="oldChild">The old child.</param>
        /// <returns></returns>
        public override XdmNode ReplaceChild(XdmNode newChild, XdmNode oldChild) {
            XdmDocument doc = this.OwnerDocument;
            if (doc == null)
                doc = this as XdmDocument;

            if (this == newChild || AncestorNode(newChild)) {
                return newChild;
            }
            if (newChild._parent != null) {
                newChild._parent.RemoveChild(newChild);
            }

            XdmDocument document2 = newChild.OwnerDocument;
            if (((document2 != null) && (document2 != doc)) && (document2 != this)) {
                return newChild;
            }
            if (oldChild == null || oldChild.ParentNode != this) {
                AppendChild(newChild);
                return newChild;
            }
            XdmNode prevNode = oldChild.PreviousSibling as XdmNode;
            newChild._nextSibling = oldChild._nextSibling;
            newChild._parent = this;
            oldChild._parent = null;
            if (prevNode != null)
                prevNode._nextSibling = newChild;

            if (_lastChild == oldChild)
                _lastChild = newChild;

            return newChild;
        }

        /// <summary>
        /// Adds the specified node to the beginning of the list of child nodes for this node.
        /// </summary>
        /// <param name="newChild">The new child.</param>
        /// <returns></returns>
        public override XdmNode PrependChild(XdmNode newChild) {
            XdmDocument doc = this.OwnerDocument;
            if (doc == null)
                doc = this as XdmDocument;

            if (this == newChild || AncestorNode(newChild)) {
                return newChild;
            }
            if (newChild._parent != null) {
                newChild._parent.RemoveChild(newChild);
            }

            XdmDocument document2 = newChild.OwnerDocument;
            if (((document2 != null) && (document2 != doc)) && (document2 != this)) {
                return newChild;
            }

            XdmNode lastNode = this.LastChild;
            XdmNode nextNode = newChild;
            if (lastNode == null) {
                nextNode._nextSibling = nextNode;
                this._lastChild = nextNode;
                nextNode._parent = this;
            } else {
                nextNode._nextSibling = lastNode._nextSibling;
                lastNode._nextSibling = nextNode;
                nextNode._parent = this;
            }
            return newChild;
        }

        public override void RemoveChild(XdmNode childNode) {
            XdmDocument doc = this.OwnerDocument;
            if (doc == null)
                doc = this as XdmDocument;

            if (this == childNode || childNode._parent != this) {
                return;
            }

            XdmDocument document2 = childNode.OwnerDocument;
            if (((document2 != null) && (document2 != doc)) && (document2 != this)) {
                return;
            }

            XdmNode lastNode = this.LastChild;
            XdmNode firstNode = this.GetFirstChild() as XdmNode;
            if (firstNode == lastNode && firstNode == childNode) {
                _lastChild = null;
                childNode._parent = null;
                childNode._nextSibling = null;
                return;
            }

            XdmNode prevNode = null, nextNode = null;
            for (XdmNode currNode = firstNode; currNode != null; currNode = currNode.NextSibling as XdmNode) {
                if (currNode == childNode) {
                    if (currNode == firstNode) {
                        nextNode = currNode.NextSibling as XdmNode;
                        lastNode._nextSibling = nextNode;
                        break;
                    } else if (currNode == lastNode) {
                        prevNode._nextSibling = firstNode;
                        break;
                    } else {
                        nextNode = currNode.NextSibling as XdmNode;
                        prevNode._nextSibling = nextNode;
                        break;
                    }
                }
                prevNode = currNode;
            }

            childNode._parent = null;
            childNode._nextSibling = null;
        }


        internal bool AncestorNode(XdmNode node) {
            for (XdmNode node2 = this._parent; (node2 != null) && (node2 != this); node2 = node2._parent) {
                if (node2 == node) {
                    return true;
                }
            }
            return false;
        }

        internal override Node GetLastChild() {
            return _lastChild;
        }

        public new XdmNode NextSibling {
            get {
                if ((base._parent != null) && (base._parent.GetLastChild() != this)) {
                    return base._nextSibling;
                }
                return null;
            }
        }

        internal override Node GetPreviousSibling() {

            for (XdmNode childNode = ((base._parent != null) ? base._parent.FirstChild : null); childNode != null; childNode = (XdmNode)childNode.NextSibling) {
                if (childNode._nextSibling == this)
                    return childNode;
            }
            return null;
        }

        public override void WriteTo(System.Xml.XmlWriter w) {
            w.WriteStartElement(this._name.Prefix, this._name.LocalName, this._name.NamespaceURI);

            for (XdmNode attr = this.FirstNS; attr != null; attr = attr._nextSibling) {
                XdmNSAttribute ns = (XdmNSAttribute)attr;
                ns.WriteTo(w);
            }

            if (this.AttributeCount > 0) {
                XdmAttribute n = this._attributes;
                for (; n != null; n = (XdmAttribute)n._nextSibling) {
                    n.WriteTo(w);
                }
            }
            if (this.FirstChild == null) {
                w.WriteEndElement();
            } else {
                this.WriteContentTo(w);
                w.WriteFullEndElement();
            }

        }
        public override void WriteContentTo(XmlWriter w) {
            for (XdmNode node = this.FirstChild as XdmNode; node != null; node = node.NextSibling as XdmNode) {
                node.WriteTo(w);
            }
        }

        public override IXdmSchemaInfo SchemaInfo {
            get {
                IXdmSchemaInfo schemaInfo = _name;
                if (schemaInfo.Validity == XmlSchemaValidity.NotKnown) {
                    XmlSchemaSet schemaSet = this.Schemas;
                    ICollection schemas = schemaSet.Schemas(this.NamespaceURI);
                    foreach (XmlSchema schema in schemas) {
                        XmlSchemaObject schemaObj = schema.Elements[this.NodeName];
                        if (schemaObj != null) {
                            XdmSchemaInfo schemaInfo2 = new XdmSchemaInfo();
                            XmlSchemaElement schemaElement = schemaObj as XmlSchemaElement;
                            schemaInfo2.SchemaElement = schemaElement;
                            schemaInfo2.Validity = XmlSchemaValidity.Valid;
                            schemaInfo2.DataType = schemaSet.GetDataType(schemaElement);
                            _name.Update(schemaInfo2);
                            return _name;
                        }
                    }
                }
                return schemaInfo;
            }
        }
        internal override Node GetOwnerDocument() {
            if (this.NodeType == XdmNodeType.Document)
                return this;
            return _name.OwnerDocument;
        }

        public new XdmDocument OwnerDocument {
            get {
                if (this.NodeType == XdmNodeType.Document)
                    return this as XdmDocument;
                return _name.OwnerDocument;
            }
        }
        #endregion
    }
}
